﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using My.Core.Enums;
using My.Core.Extensions;
using My.Core.Options;
using My.Core.UnifyResult;
using My.Framework.AspNetCore;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Text.Json;

namespace My.Core.Jwt
{
    /// <summary>
    /// 程序启动器 - 自动化注册服务
    /// </summary>
    public class AuthenticationStartup : StartupModule<AuthenticationStartup>
    {
        /// <summary>
        /// 程序启动器
        /// </summary>
        public AuthenticationStartup() => Order = 1;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webApplicationBuilder"></param>
        public override void ConfigureServices(WebApplicationBuilder webApplicationBuilder)
        {
            var services = webApplicationBuilder.Services;

			services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddCookie()
            .AddJwtBearer("SimpleApp", options =>
            {
                options.TokenValidationParameters = ValidParameters();
            })
            .AddJwtBearer(o =>
            {
                o.TokenValidationParameters = ValidParameters();
                o.Events = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        // 如果过期，把过期信息添加到头部
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            Console.WriteLine("jwt过期了");
                            context.Response.Headers.Add("Token-Expired", "true");
                        }

                        return Task.CompletedTask;
                    },
					OnChallenge = async context =>
					{
						// refresh token

						// JWT Bearer 认证失败 处理 401 状态码 返回自定义数据 Json
						context.Response.StatusCode = StatusCodes.Status200OK;
						context.Response.ContentType = "application/json";

						ExceptionMetadata apiResult = new ExceptionMetadata
						{
							StatusCode = (int)ResultCode.SUCCESS,
							ErrorCode = (int)ResultCode.DENY,
							OriginErrorCode = (int)ResultCode.DENY,
							Errors = ResultCode.DENY.GetType().GetField(Enum.GetName(ResultCode.DENY)).GetCustomAttribute<DescriptionAttribute>().Description,
						};

						string responseResult = JsonSerializer.Serialize(apiResult).ToLower();
						await context.Response.WriteAsync(responseResult);
						// 标识处理了响应
						context.HandleResponse();
					},
					OnForbidden = async context =>
					{
						context.Response.StatusCode = StatusCodes.Status200OK;
						context.Response.ContentType = "application/json";

						// JWT Bearer 认证失败 处理 403 状态码 返回自定义数据 Json
						ExceptionMetadata apiResult = new ExceptionMetadata
                        {
                            StatusCode = (int)ResultCode.SUCCESS,
                            ErrorCode = (int)ResultCode.FORBIDDEN,
                            OriginErrorCode = (int)ResultCode.FORBIDDEN,
                            Errors = ResultCode.FORBIDDEN.GetType().GetField(Enum.GetName(ResultCode.FORBIDDEN)).GetCustomAttribute<DescriptionAttribute>().Description,
						};

						string responseResult = JsonSerializer.Serialize(apiResult).ToLower();
						await context.Response.WriteAsync(responseResult);
					}
				};
            });

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webApplication"></param>
        public override void Configure(WebApplication webApplication)
        {
            //使可以多次多去body内容
            webApplication.Use((context, next) =>
            {
                context.Request.EnableBuffering();
                // 允许在Url中添加access_token=[token]，直接在浏览器中访问
                if (context.Request.Query.TryGetValue("access_token", out var token))
                {
                    try
                    {
                        var HeadersAuthorization = context.Request.Headers.Where(x => x.Key == "Authorization").First().Value.ToString();
                        if(HeadersAuthorization == null)
                        {
							context.Request.Headers.Add("Authorization", $"Bearer {token}");
						}
						
					}
                    catch (Exception)
                    {

                    }
                    
                }
                return next();
            });

            //app.UseAuthentication会启用Authentication中间件，该中间件会根据当前Http请求中的Cookie信息来设置HttpContext.User属性（后面会用到），
            //所以只有在app.UseAuthentication方法之后注册的中间件才能够从HttpContext.User中读取到值，
            //这也是为什么上面强调app.UseAuthentication方法一定要放在下面的app.UseMvc方法前面，因为只有这样ASP.NET Core的MVC中间件中才能读取到HttpContext.User的值。
            //1.先开启认证
            webApplication.UseAuthentication();
        }

        /// <summary>
        /// 验证Token
        /// </summary>
        /// <returns></returns>
        public TokenValidationParameters ValidParameters()
        {
            JwtSettings jwtSettings = new();
            AppSettings.Bind("JwtSettings", jwtSettings);

            if (jwtSettings == null || jwtSettings.SecretKey.IsEmpty())
            {
                throw new Exception("JwtSettings获取失败");
            }
            var key = Encoding.ASCII.GetBytes(jwtSettings.SecretKey);

            var tokenDescriptor = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidIssuer = jwtSettings.Issuer,
                ValidAudience = jwtSettings.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateLifetime = true,//是否验证Token有效期，使用当前时间与Token的Claims中的NotBefore和Expires对比
                ClockSkew = TimeSpan.FromSeconds(30),
                //RequireExpirationTime = true,//过期时间
            };
            return tokenDescriptor;
        }
    }
}
